/**
 * wrap.h内函数的实现；
 * 注意对accept的封装；
 * @author: sundx
 * @date: 2020-05-29 11:07:30
 * @Last Modified by: sundx
 * @Last Modified time: 2020-05-30 23:08:30
 */
#include "wrap.h"

namespace sdx {
void printError(const char *mes) {
  perror(mes);
  exit(EXIT_FAILURE);
}

int Socket(int domain, int type, int protocol) {
  int ret = socket(domain, type, protocol);
  if (ret < 0) {
    printError("socket crated failed");
  }
  return ret;
}

int Bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
  int ret = bind(sockfd, addr, addrlen);
  if (ret < 0) {
    printError("bind failed");
  }
  return ret;
}

int Listen(int sockfd, int backlog) {
  int ret = listen(sockfd, backlog);
  if (ret < 0) {
    printError("listen failed");
  }
  return ret;
}

int Accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
again:
  int ret = accept(sockfd, addr, addrlen);
  if (ret < 0) {
    // 慢系统调用，在阻塞等待客户端的时候被系统信号打断
    if ((errno == ECONNABORTED) || (errno == EINTR)) {
      goto again;
    } else {
      printError("Accept failed");
    }
  }
  return ret;
}

int Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {

  int ret = connect(sockfd, addr, addrlen);
  if (ret < 0) {
    printError("Connect failed");
  }
  return ret;
}

int serverSocketBindListen() { // 完成服务端创建fd，绑定ip，并监听，返回服务端监听fd
  int listenFd = Socket(AF_INET, SOCK_STREAM, 0); // 创建监听socket
  int flag = 1; // 表示生效，端口复用；
  // 设置端口可重复使用；
  int ret = setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *)&flag,
                       sizeof(flag));
  if (ret == -1) {
    printError("set listenFd failed...");
  }
  struct sockaddr_in serverAddr; // 服务端地址结构体
  socklen_t serverAddrStructLen = sizeof(serverAddr); //地址结构体所占空间
  bzero(&serverAddr, sizeof(serverAddr));             // 初始化结构体
  serverAddr.sin_family = AF_INET;                    // ip协议
  serverAddr.sin_port = htons(SERVER_PORT);           // 端口
  serverAddr.sin_addr.s_addr = INADDR_ANY;            // ip
  // 绑定地址和端口
  Bind(listenFd, (sockaddr *)&serverAddr, serverAddrStructLen);
  Listen(listenFd, LISTEN_WINDOW); // 设置监听窗口为20，允许20个tcp连接；
  return listenFd;
}

void setFdUnblock(int fd) { //将文件描述符设置为非阻塞；
  int flag = fcntl(fd, F_GETFD);
  if (flag == -1)
    printError("get fd flag failed...");
  flag = fcntl(fd, F_SETFD, flag | O_NONBLOCK);
  if (flag == -1)
    printError("get fd flag failed...");
  return;
}

int Epoll_create(int size) { //创建epoll文件描述符，红黑树的树根；
  int epollRoot = epoll_create(20);
  if (epollRoot == -1)
    printError("epoll create error...");
  return epollRoot;
}

// epoll 控制函数，添加，修改，删除；
void Epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) {
  int ret = epoll_ctl(epfd, op, fd, event);
  if (ret == -1)
    printError("epoll ctl error...");
  return;
}

// EPoll_wait 错误封装；
int Epoll_wait(int epfd, struct epoll_event *events, int maxevents,
               int timeout) {
  int activeFd = epoll_wait(epfd, events, maxevents, timeout);
  if (activeFd == -1) {
    printError("epoll wait error...");
  }
  return activeFd;
}

} // namespace sdx
